﻿using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Writers;
using System.ComponentModel;
using System.Reflection;

namespace OpenApiExtensions
{
    internal class FixEnumSchemaTransformer : IOpenApiSchemaTransformer
    {
        public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context,
            CancellationToken cancellationToken)
        {

            if (!(context.JsonTypeInfo.Type.IsEnum
               || (context.JsonTypeInfo.Type.IsGenericType && Nullable.GetUnderlyingType(context.JsonTypeInfo.Type)?.IsEnum == true)))
                return Task.CompletedTask;


            var enumType = context.JsonTypeInfo.Type.IsGenericType && Nullable.GetUnderlyingType(context.JsonTypeInfo.Type)?.IsEnum == true
                ? Nullable.GetUnderlyingType(context.JsonTypeInfo.Type) : context.JsonTypeInfo.Type;

            //This is because of a bug that doesn't populate this.
            schema.Enum.Clear(); //Hack just in case they fix the bug.
                                 // foreach (var name in Enum.GetNames(enumType)) schema.Enum.Add(new OpenApiString(name));

            if (enumType != null)
                Enum.GetNames(enumType)
                      .ToList()
                      .ForEach(name =>
                      {
                          Enum e = (Enum)Enum.Parse(enumType, name);
                          //model.Enum.Add(new Open)
                          schema.Description += $"\n\n{Convert.ToInt32(Enum.Parse(enumType, name))} = {name} ({e.ToDescription()})";

                          schema.Enum.Add(new OpenApiInteger(Convert.ToInt32(Enum.Parse(enumType, name))));
                          schema.Example =
                          context.JsonTypeInfo.Type.IsGenericType && Nullable.GetUnderlyingType(context.JsonTypeInfo.Type)?.IsEnum == true ?
                          new OpenApiNull() :

                          new OpenApiString($"{Convert.ToInt32(Enum.Parse(enumType, name))}");
                      });


            return Task.CompletedTask;
        }

    }

    public class DefaultValueSchemaTransformer : IOpenApiSchemaTransformer
    {
        bool IsBasicTypeExtended(Type type)
        {
            // 检查是否为原始类型
            if (type.IsPrimitive)
            {
                return true;
            }

            // 检查是否为其他我们认为是“基本”的类型
            if (type == typeof(string) ||
                  type == typeof(Nullable<int>) ||
                  type == typeof(Nullable<float>) ||
                  type == typeof(Nullable<long>) ||
                  type == typeof(Nullable<Int64>) ||
                type == typeof(decimal) ||
                  type == typeof(Nullable<decimal>) ||
                type == typeof(DateTime) ||
                 type == typeof(Nullable<DateTime>) ||
                type == typeof(TimeSpan) ||
                 type == typeof(Nullable<TimeSpan>) ||
                type == typeof(Guid))
            {
                return true;
            }

            // 默认情况下返回 false
            return false;
        }

        public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context,
             CancellationToken cancellationToken)
        {
            if (schema.Properties == null)
            {
                return Task.CompletedTask;
            }


            foreach (var propertyInfo in context.JsonTypeInfo.Type.GetProperties())
            {
                var defaultValue = (propertyInfo.GetCustomAttribute<DefaultValueAttribute>())?.Value;
                object? objvalue = null;
                if (context.JsonTypeInfo.Type.IsClass && !context.JsonTypeInfo.Type.IsAbstract
                    && !context.JsonTypeInfo.Type.IsGenericType && !context.JsonTypeInfo.Type.IsArray
                    && context.JsonTypeInfo.Type != typeof(string) && context.JsonTypeInfo.Type.IsPublic)
                {
                    try
                    {
                        var obj = Activator.CreateInstance(context.JsonTypeInfo.Type);

                        if (!IsNullableType(propertyInfo.PropertyType))
                        {
                            objvalue = propertyInfo.GetValue(obj);
                        }



                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                    }

                }


                var val = defaultValue ?? objvalue;
                if (val == null && !IsBasicTypeExtended(propertyInfo.PropertyType))
                {
                    continue;
                }
                //var val = defaultValue;
                foreach (var (key, value) in schema.Properties)
                {
                    if (propertyInfo.Name.ToLower().Equals(key.ToLower()))
                    {
                        try
                        {
                            if (val == null)
                            {
                                value.Example = new OpenApiNull();
                            }
                            else if (val is int intValue)
                            {
                                value.Example = new OpenApiInteger(intValue);
                                break;
                            }
                            else if (val is string stringValue)
                            {
                                /* if (stringValue == "")
                                 {
                                     Console.WriteLine($"stringValue: {stringValue}");
                                 }
                                 else if (stringValue== "string")
                                 {
                                     Console.WriteLine($"stringValue: {stringValue}");

                                 }*/
                                value.Example = new OpenApiString(stringValue);
                                break;
                            }

                            else if (val is DateTime dateTimeValue)
                            {
                                if (dateTimeValue == default(DateTime))
                                {
                                    dateTimeValue = DateTime.Now;
                                }
                                var v = JSON.Serialize(dateTimeValue);
                                value.Example = new OpenApiString(v.Substring(1, v.Length - 2).Replace("\\", ""));
                                break;
                            }

                            else if (val is Boolean boolValue)
                            {
                                value.Example = new OpenApiBoolean(boolValue);
                                break;
                            }
                            else
                            {
                                /*if (val != null && val.ToString() == "string")
                                {
                                    Console.WriteLine($"val: {val}");
                                }*/


                                // new  OpenApiJsonWriter()
                                //    value.Example =    OpenApiAnyFactory.CreateFromJson(JSON.Serialize(val));
                                break;
                            }
                        }
                        catch (Exception ex)
                        {

                            Console.WriteLine($"Error: {ex.ToString()}");
                        }

                    }
                }
            }

            return Task.CompletedTask;
        }

        bool IsNullableType(Type that)
        {
            return !(that == null) && !that.IsArray && that.FullName.StartsWith("System.Nullable`1[");
        }
    }


    internal class FixNullbleSchemaTransformer : IOpenApiSchemaTransformer
    {
        public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context,
            CancellationToken cancellationToken)
        {
            //Console.WriteLine(context.JsonTypeInfo.Type.Name);
            /*  var type = context.JsonTypeInfo.Type;
              if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
              {
                  schema.Example = new OpenApiNull();
              }*/
            FixNullableProperties(schema, context);
            return Task.CompletedTask;
        }
        private static void FixNullableProperties(OpenApiSchema schema, OpenApiSchemaTransformerContext context)
        {
            
            var notNullableProperties = schema
    .Properties
    .Where(x => !x.Value.Nullable && x.Value.Default == default && !schema.Required.Contains(x.Key))
    .ToList();
            foreach (var property in notNullableProperties)
            {
                schema.Required.Add(property.Key);
            }

            /* foreach (var property in schema.Properties)
             {

                 var field = context.JsonTypeInfo.Type
                     .GetMembers(BindingFlags.Public | BindingFlags.Instance)
                     .FirstOrDefault(x =>
                         string.Equals(x.Name, property.Key, StringComparison.InvariantCultureIgnoreCase));

                 if (field != null)
                 {
                     var fieldType = field switch
                     {
                         FieldInfo fieldInfo => fieldInfo.FieldType,
                         PropertyInfo propertyInfo => propertyInfo.PropertyType,
                         _ => throw new NotSupportedException(),
                     };

                     property.Value.Nullable = fieldType.IsValueType
                         ? Nullable.GetUnderlyingType(fieldType) != null
                         : !field.IsNonNullableReferenceType();


                     if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
                     {
                         schema.Example = new OpenApiNull();
                     }
                 }

             }*/
        }

    }

    public static class MyClass
    {
        public static bool IsNonNullableReferenceType(this MemberInfo memberInfo)
        {
            if (((memberInfo.MemberType == MemberTypes.Field) ? ((FieldInfo)memberInfo).FieldType : ((PropertyInfo)memberInfo).PropertyType).IsValueType)
            {
                return false;
            }

            object nullableAttribute = memberInfo.GetNullableAttribute();
            if (nullableAttribute == null)
            {
                return memberInfo.GetNullableFallbackValue();
            }

            FieldInfo field = nullableAttribute.GetType().GetField("NullableFlags");
            if ((object)field != null && field.GetValue(nullableAttribute) is byte[] array && array.Length >= 1 && array[0] == 1)
            {
                return true;
            }

            return false;
        }
        private static bool GetNullableFallbackValue(this MemberInfo memberInfo)
        {
            Type[] array = ((!memberInfo.DeclaringType.IsNested) ? new Type[1] { memberInfo.DeclaringType } : new Type[2]
            {
            memberInfo.DeclaringType,
            memberInfo.DeclaringType.DeclaringType
            });
            for (int i = 0; i < array.Length; i++)
            {
                object obj = array[i].GetCustomAttributes(inherit: false).FirstOrDefault((object attr) => string.Equals(attr.GetType().FullName, "System.Runtime.CompilerServices.NullableContextAttribute"));
                if (obj != null)
                {
                    FieldInfo field = obj.GetType().GetField("Flag");
                    if ((object)field != null && field.GetValue(obj) is byte b && b == 1)
                    {
                        return true;
                    }

                    return false;
                }
            }

            return false;
        }
        private static object GetNullableAttribute(this MemberInfo memberInfo)
        {
            return memberInfo.GetCustomAttributes().FirstOrDefault((Attribute attr) => string.Equals(attr.GetType().FullName, "System.Runtime.CompilerServices.NullableAttribute"));
        }
    }
}
